package com.gjzhao.fmmall.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.gjzhao.fmmall.constant.ConstantInfo;
import com.gjzhao.fmmall.dao.UsersMapper;
import com.gjzhao.fmmall.entity.Users;
import com.gjzhao.fmmall.service.UsersService;
import com.gjzhao.fmmall.utils.JwtUtil;
import com.gjzhao.fmmall.utils.MD5Utils;
import com.gjzhao.fmmall.vo.RespCode;
import com.gjzhao.fmmall.vo.RespResultVO;
import com.gjzhao.fmmall.vo.ResponseResultVO;
import com.gjzhao.fmmall.vo.TokenVO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.UnsupportedJwtException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

/**
 * @Description: 用户管理-实现类
 * @Author: gjzhao
 * @Date Created in 2021-10-30 2:50 下午
 */
@Service
//@Scope("singleton") //防止注入多个Service，可指定单例，可将该注解提到Controller层，默认为单例
public class UsersServiceImpl implements UsersService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private UsersMapper usersMapper;
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //SpringBoot集成redis提供了两种连接redis的客户端
    //@Autowired
    //private RedisTemplate redisTemplate;


    @Override
    public ResponseResultVO getUserByUsername(String username, String password) {

        ResponseResultVO responseResultVO;
        //用户名是否存在
        Example example = new Example(Users.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("username", username);
        List<Users> usersList = usersMapper.selectByExample(example);

        if (usersList.size() == 0) {
            responseResultVO = new ResponseResultVO(RespCode.FAILED.getCode(), "用户信息不存在", null);
        } else {
            //对用户输入对密码进行MD5加密，然后与数据库的密码进行比对
            //TODO 可以考虑再进行一层加盐操作
            String md5Pwd = MD5Utils.md5(password);
            if (md5Pwd.equals(usersList.get(0).getPassword())) {
                responseResultVO = new ResponseResultVO(RespCode.SUCCESS.getCode(), "登录成功", usersList.get(0));
            } else {
                responseResultVO = new ResponseResultVO(RespCode.FAILED.getCode(), "密码错误，登录失败", null);
            }
        }
        return responseResultVO;
    }

    @Override
    public ResponseResultVO getUserByUsernameByToken(String username, String password) {

        ResponseResultVO responseResultVO;

        //用户名是否存在
        Example example = new Example(Users.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("username", username);
        List<Users> usersList = usersMapper.selectByExample(example);

        if (usersList.size() == 0) {
            responseResultVO = new ResponseResultVO(RespCode.FAILED.getCode(), "用户信息不存在", null);
        } else {
            //对用户输入对密码进行MD5加密，然后与数据库的密码进行比对
            String md5Pwd = MD5Utils.md5(password);
            if (md5Pwd.equals(usersList.get(0).getPassword())) {
                //生成token一并返回给前端

                /*
                JwtBuilder builder = Jwts.builder();

                Map<String, Object> map = new HashMap<>();
                map.put("admin", "admin666");

                String token = builder.setId(usersList.get(0).getUserId() + "") //唯一id
                                      .setSubject(username) //token主题
                                      .setIssuedAt(new Date()) //生成时间
                                      //.setClaims(map) //可传入用户角色权限相关信息 注意如果加上了参数，获取不到subject部分
                                      .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000)) //过期时间
                                      .signWith(SignatureAlgorithm.HS256, ConstantInfo.JWT_TOKEN_KEY) //密钥与算法
                                      .compact();


                Jws<Claims> jws = Jwts.parser().setSigningKey(ConstantInfo.JWT_TOKEN_KEY).parseClaimsJws(token);
                Claims claims = jws.getBody();
                System.out.println("------------" + claims + "-----------");
                */

                //使用自己封装的Token工具类进行生成与解析
                String token = jwtUtil.getToken(usersList.get(0).getUserId() + "", username,ConstantInfo.TOKEN_LOGIN_ROLE_ADMIN);

                Claims claims = jwtUtil.parseToken(token);

                logger.info("-----------------" + String.valueOf(claims) + "-------------------");


                //以token为key，将当前用户登录信息放在redis中(不同登录用户会有不同的token值)，同时设置过期时间为30分钟
                Users user = usersList.get(0);
                String userStr = JSONUtil.toJsonStr(user);
                stringRedisTemplate.boundValueOps(token).set(userStr, 30, TimeUnit.MINUTES);
                logger.info("存放token:{},到redis，存放的用户信息:{}",token,user.toString());

                responseResultVO = new ResponseResultVO(RespCode.SUCCESS.getCode(), token, usersList.get(0));
            } else {
                responseResultVO = new ResponseResultVO(RespCode.FAILED.getCode(), "密码错误，登录失败", null);
            }
        }
        return responseResultVO;
    }

    /**
     * 校验token
     *
     * @param token
     * @return
     */
    @Override
    public ResponseResultVO checkToken(String token) {
        //判空
        if (StrUtil.isEmpty(token)) {
            return new ResponseResultVO(RespCode.FAILED.getCode(), "请输入需要校验的token", null);
        }

        try {
            //token校验
            JwtParser parser = Jwts.parser();
            parser.setSigningKey(ConstantInfo.JWT_TOKEN_KEY);//放入token的key

            //校验token，如果token正确（密码正确且在有效期），则正常执行，否则抛出异常
            Jws<Claims> claimsJws = parser.parseClaimsJws(token);

            //获取创建token时传入的相关数据
            Claims body = claimsJws.getBody();


            Map<String, String> map = new HashMap<>();
            map.put("admin", body.get("admin", String.class));


            //封装返回的token数据
            TokenVO<String, String> tokenVO = new TokenVO<String, String>();
            tokenVO.setSubject(body.getSubject());
            tokenVO.setId(body.getId());
            tokenVO.setIssuedAt(body.getIssuedAt());
            tokenVO.setExpiration(body.getExpiration());
            tokenVO.setMap(map);

            return new ResponseResultVO(RespCode.SUCCESS.getCode(), "success", tokenVO);

        } catch (ExpiredJwtException e) {
            return new ResponseResultVO(RespCode.FAILED.getCode(), "token已过期", null);
        } catch (UnsupportedJwtException e) {
            return new ResponseResultVO(RespCode.FAILED.getCode(), "token不合法", null);
        } catch (Exception e) {
            return new ResponseResultVO(RespCode.FAILED.getCode(), "请重新输入token进行校验", null);
        }

    }

    /**
     * 校验token：这里使用了拦截器，如果该请求能进来，说明token还未过期，可以正常使用
     * @param token
     * @return
     */
    @Override
    public ResponseResultVO checkTokenByInterceptor(String token) {
        return new ResponseResultVO(RespCode.SUCCESS.getCode(), "success", null);
    }

    @Override
    @Transactional //使用数据库的默认隔离级别，防止在事务A刚查询完用户名是否存在时，事务B就添加了用户
    public ResponseResultVO insertUser(String username, String password) {
        ResponseResultVO responseResultVO;

        //需要确保锁对象的唯一性
        synchronized (this) {
            //查询用户名是否存在
            Example example = new Example(Users.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("username", username);
            List<Users> usersList = usersMapper.selectByExample(example);
            if (usersList.size() == 0) {
                //密码不能直接存在数据库，要使用MD5加密处理，存储加密后的密码
                String md5Pwd = MD5Utils.md5(password);
                Users user = new Users();
                user.setUsername(username);
                user.setPassword(md5Pwd);
                user.setUserImg("img/default_user_img.png");
                user.setUserRegtime(new Date());
                user.setUserModtime(new Date());
                int result = usersMapper.insertUseGeneratedKeys(user);
                if (result > 0) {
                    responseResultVO = new ResponseResultVO(RespCode.SUCCESS.getCode(), "注册成功", user);
                    logger.debug("-------注册用户 success:{}------",user);
                } else {
                    responseResultVO = new ResponseResultVO(RespCode.FAILED.getCode(), "注册失败", null);
                    logger.debug("-------注册用户 failed:{}------",user);
                }
            } else {
                responseResultVO = new ResponseResultVO(RespCode.FAILED.getCode(), "用户名字已存在", null);
                logger.debug("-------注册用户 failed,用户名已存在:name={}------",username);
            }
        }
        return responseResultVO;
    }

    /**
     * 根据用户id查询用户信息
     * @param userId
     * @return
     */
    @Override
    public ResponseResultVO selectUserByUserId(String userId) {

        //参数校验
        if (StrUtil.isBlank(userId)) {
            return new ResponseResultVO(RespCode.FAILED.getCode(), "用户id不能为空", null);
        }

        Users user = usersMapper.selectByPrimaryKey(userId);
        //用户密码置空不返回
        user.setPassword("");

        return new ResponseResultVO(RespCode.SUCCESS.getCode(), "success", user);
    }

    /**
     *  根据账户密码删除用户
     * @param username
     * @param password
     * @return
     */
    @Override
    public RespResultVO deleteUser(String username, String password) {

        //校验参数
        if (StrUtil.isBlank(username) && StrUtil.isBlank(password)) {
            return RespResultVO.validateFailed();
        }

        Example example = new Example(Users.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("username",username);

        List<Users> users = usersMapper.selectByExample(example);

        //判断用户是否存在
        if (CollectionUtil.isEmpty(users)) {
            logger.debug("-------删除用户 failed,用户不存在:username={}",username);
            return RespResultVO.failed(RespCode.FAILED,"用户不存在");
        }

        int deleteResult = usersMapper.deleteByPrimaryKey(users.get(0).getUserId());
        if (deleteResult > 0) {
            logger.debug("-------删除用户 success:username={}",username);
            return RespResultVO.success();
        }else{
            logger.debug("-------删除用户 failed:username={}",username);
            return RespResultVO.failed(RespCode.FAILED);
        }
    }
}
